#include "canonizador.h"

void ExpARI::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Stm::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Global::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Frame::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void FrameMips::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Fragment::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void FragmentList::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void LiteralARI::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Procedure::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void ListaAcesso::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void AcessoLocal::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void InFrame::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void InReg::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Temp::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void TempList::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Label::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void LabelList::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void ExpARIList::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Const::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void ConstF::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Name::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void BinOp::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Mem::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void CallARI::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Eseq::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Move::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Jump::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void CJump::accept(VisitorCanonizador* visitor) { visitor->visit(this); }
void Seq::accept(VisitorCanonizador* visitor) { visitor->visit(this); }

int flagCanonizouExp = 1;
int flagCanonizouStm = 1;

int setFlagCanonizouExp(int f) { flagCanonizouExp = f; }
int setFlagCanonizouStm(int f) { flagCanonizouStm = f; }

int getFlagCanonizouExp() { return flagCanonizouExp; }
int getFlagCanonizouStm() { return flagCanonizouStm; }

int isNop(Stm *stm) {
    ExpARI* exp = dynamic_cast<ExpARI*>(stm);
    return (exp != NULL && dynamic_cast<Const*>(exp) != NULL);
}

int comuta(Stm* exp, Stm* stm) {
    return (dynamic_cast<Name*>(exp) || (dynamic_cast<Const*>(exp)) || isNop(stm));
}

Stm* Canonazacao_Retira_Eseq_Stm(Stm *stm){

    CJump *cJump = dynamic_cast<CJump*>(stm);
    if(cJump != NULL) {
        Eseq *eseq1 = dynamic_cast<Eseq*>(cJump->e1);
        Eseq *eseq2 = dynamic_cast<Eseq*>(cJump->e2);

        if (eseq1 != NULL){
            cJump->e1 = eseq1->exp;
            Seq *seq1 = new Seq(eseq1->stm, cJump);
            flagCanonizouStm++;
            return seq1;
        }
        if (eseq2){
            if(comuta(cJump->e1,eseq2->stm)){
                cJump->e2 = eseq2->exp;
                Seq *seq1 = new Seq(eseq2->stm, cJump);
                flagCanonizouStm++;
                return seq1;
            }else{
                Temp *temp = new Temp();
                cJump->e2 = eseq2->exp;
                Seq *seq1 = new Seq(eseq2->stm, cJump);
                Seq *seq2 = new Seq(new Move(temp, cJump->e1), seq1);
                cJump->e1 = temp;
                flagCanonizouStm++;
                return seq2;
            }
        }
    }

    Move *move = dynamic_cast<Move*>(stm);
    if(move != NULL) {
        Eseq *eseq1 = dynamic_cast<Eseq*>(move->t);
        Eseq *eseq2 = dynamic_cast<Eseq*>(move->e);

        if (eseq1){
            move->t = eseq1->exp;
            Seq *seq1 = new Seq(eseq1->stm,move);
            flagCanonizouStm++;
            return seq1;
        }
        if (eseq2){
            Temp* t = new Temp();
            Seq* seq1 = new Seq(new Move(t, move->t),
                new Seq(eseq2->stm, new Move(t, eseq2->exp)));
            flagCanonizouStm++;
            return seq1;
        }
    }

    Jump* jump = dynamic_cast<Jump*>(stm);
    if(jump != NULL) {
        Eseq* eseq1 = dynamic_cast<Eseq*>(jump->exp);
        if(eseq1 != NULL) {
            jump->exp = (ExpARI*)eseq1->exp;
            Seq* seq1 = new Seq(eseq1->stm, jump);
            flagCanonizouStm++;
            return seq1;
        }
    }

    ExpARI* exp = dynamic_cast<ExpARI*>(stm);
    if(exp != NULL) {
        Eseq* eseq = dynamic_cast<Eseq*>(exp);
        if(eseq != NULL){
            Seq* seq1 = new Seq(eseq->stm, eseq->exp);
            flagCanonizouStm++;
            return seq1;
       }
   }
    return stm;
}

ExpARI* Canonazacao_Retira_Eseq_Expr(ExpARI *exp){
    Eseq* eseq = dynamic_cast<Eseq*>(exp);
    if(eseq != NULL) {
        Eseq* _eseq = dynamic_cast<Eseq*>(eseq->exp);
        if(_eseq != NULL) {
            Seq* seq = new Seq(eseq->stm, _eseq->stm);
            _eseq->stm = seq;
            flagCanonizouExp++;
            return _eseq;
        }
    }

    BinOp* binOp = dynamic_cast<BinOp*>(exp);
    if(binOp) {
        Eseq* eseq1 = dynamic_cast<Eseq*>(binOp->e1);
        Eseq* eseq2 = dynamic_cast<Eseq*>(binOp->e2);
        if(eseq1 != NULL){
            ExpARI* aux = eseq1->exp;
            binOp->e1 = aux;
            eseq1->exp = binOp;
            flagCanonizouExp++;
            return eseq1;
        }
        if (eseq2){
            if(comuta(binOp->e1, eseq2->stm)) {
                ExpARI* aux = eseq2->exp;
                binOp->e1 = aux;
                eseq2->exp = binOp;
                flagCanonizouExp++;
                return eseq2;
            } else {
                Temp* temp = new Temp();
                Eseq* eseq3 = new Eseq(new Move(temp, (ExpARI*)binOp->e1), eseq2);
                binOp->e1 = temp;
                binOp->e2 = eseq2->exp;
                eseq2->exp = binOp;
                flagCanonizouExp++;
                return eseq3;
            }
        }
    }

    Mem* mem = dynamic_cast<Mem*>(exp);
    if(mem != NULL) {
        Eseq* eseq1 = dynamic_cast<Eseq*>(mem->e);
        if(eseq1 != NULL) {
            ExpARI* aux = eseq1->exp;
            mem->e = aux;
            eseq1->exp = mem;
            flagCanonizouExp++;
            return eseq1;
        }
    }

    CallARI* call = dynamic_cast<CallARI*>(exp);
    if(call != NULL) {
        if(!(call->canonizado == 1)) {
            Temp* temp = new Temp();
            call->canonizado = 1;
            Eseq* eseq1 = new Eseq(new Move(temp, call), temp);
            flagCanonizouExp++;
            return eseq1;
        }
    }
    return exp;
}

void VisitorCanonizador::visit(ExpARI* exp) { }

void VisitorCanonizador::visit(Stm* stm) { }

void VisitorCanonizador::visit(Global* global) { }

void VisitorCanonizador::visit(Frame* frame) { }

void VisitorCanonizador::visit(FrameMips* frameMips) {
	if(frameMips->rotulo != NULL) frameMips->rotulo->accept(this);
	if(frameMips->dadosLocais != NULL) frameMips->dadosLocais->accept(this);
}

void VisitorCanonizador::visit(FragmentList* fragmentList) {
    if(fragmentList->fragment != NULL) fragmentList->fragment->accept(this);
    if(fragmentList->proximo != NULL) fragmentList->proximo->accept(this);
}

void VisitorCanonizador::visit(Fragment* fragment) { }

void VisitorCanonizador::visit(LiteralARI* literalARI) { }

void VisitorCanonizador::visit(Procedure* procedure) {
    if(procedure->frame) procedure->frame->accept(this);
    if(procedure->body) procedure->body->accept(this);
}

void VisitorCanonizador::visit(ListaAcesso *lista) {
    if (lista->acessoLocal) lista->acessoLocal->accept(this);
    if (lista->proximo) lista->proximo->accept(this);
}

void VisitorCanonizador::visit(AcessoLocal *acess){ }

void VisitorCanonizador::visit(InFrame *inframe) {}

void VisitorCanonizador::visit(InReg *inreg) {
    if (inreg->temp) inreg->temp->accept(this);
}
void VisitorCanonizador::visit(Temp *temp){}

void VisitorCanonizador::visit(TempList *tlist){}

void VisitorCanonizador::visit(Label *label) {}
void VisitorCanonizador::visit(LabelList *lista) {}

void VisitorCanonizador::visit(ExpARIList* expARIList) {
    if (expARIList->exp) {
		expARIList->exp = Canonazacao_Retira_Eseq_Expr(expARIList->exp);
		expARIList->exp->accept(this);
	}
    if (expARIList->proximo) expARIList->proximo->accept(this);
}
void VisitorCanonizador::visit(Const *c) {}
void VisitorCanonizador::visit(ConstF *constf) {}

void VisitorCanonizador::visit(Name* nome){
    if (nome->label) nome->label->accept(this);
}

void VisitorCanonizador::visit(BinOp* binOp) {

	if (binOp->e1) {
		binOp->e1 = Canonazacao_Retira_Eseq_Expr((ExpARI*)binOp->e1);
		binOp->e1->accept(this);
	}
	if (binOp->e2) {
		binOp->e2 = Canonazacao_Retira_Eseq_Expr((ExpARI*)binOp->e2);
        binOp->e2->accept(this);
	}
}

void VisitorCanonizador::visit(Mem* mem) {
	if(mem->e != NULL) {
		mem->e = Canonazacao_Retira_Eseq_Expr(mem->e);
		mem->e->accept(this);
	}
}

void VisitorCanonizador::visit(CallARI* call) {
	if(call->funcao != NULL) {
		call->funcao = Canonazacao_Retira_Eseq_Expr(call->funcao);
		call->funcao->accept(this);
	}
    if (call->argumentos) call->argumentos->accept(this);
}

void VisitorCanonizador::visit(Eseq *eseq) {
	if (eseq->stm != NULL) {
		eseq->stm = Canonazacao_Retira_Eseq_Stm(eseq->stm);
		eseq->stm->accept(this);
	}
	if (eseq->exp != NULL) {
		eseq->exp = Canonazacao_Retira_Eseq_Expr(eseq->exp);
		(eseq->exp)->accept(this);
	}
}

void VisitorCanonizador::visit(Move *move) {
	if (move->e) {
		move->e = Canonazacao_Retira_Eseq_Expr(move->e);
		move->e->accept(this);
	}
	if (move->t) {
		move->t = Canonazacao_Retira_Eseq_Expr(move->t);
		(move->t)->accept(this);
	}
}

void VisitorCanonizador::visit(Jump *jump) {
	/*if (jump->label) {
		jump->label = (Label*)Canonazacao_Retira_Eseq_Expr(jump->label);
		(jump->label)->accept(this);
	}*/
}

void VisitorCanonizador::visit(CJump *cJump) {
    if (cJump->e1) {
		cJump->e1 = Canonazacao_Retira_Eseq_Expr(cJump->e1);
        cJump->e1->accept(this);
	}
	if (cJump->e2) {
		cJump->e2 = Canonazacao_Retira_Eseq_Expr(cJump->e2);
        cJump->e2->accept(this);
	}
    if (cJump->t) cJump->t->accept(this);
    if (cJump->f) cJump->f->accept(this);
}

void VisitorCanonizador::visit(Seq *seq) {
	if (seq->s1) {
		seq->s1 = Canonazacao_Retira_Eseq_Stm(seq->s1);
        seq->s1->accept(this);
	}
	if (seq->s2) {
		seq->s2 = Canonazacao_Retira_Eseq_Stm(seq->s2);
        seq->s2->accept(this);
	}
}
